importScripts("libffmpeg.js");
var videoCallback = null
var audioCallback = null
var isInited = false;
var chunkSize = 65536; //64K
var videoHeaderSize =  65536;//64k
var nowVideoheaderSize = 0;

var LOG_LEVEL_JS = 0;
var LOG_LEVEL_WASM = 1;
var LOG_LEVEL_FFMPEG = 2;
var DECODER_G711A = 0;
var DECODER_AAC = 1;
var decoder_type = DECODER_G711A;
var pts = 0;
var videoSize = 0;

var audioSampleRate = 8000;
var audioChannelCount = 1;

var isDecoderInited = false;
var isVideoInited = false;
var isAudioInited = false;

var videoCacheBuffer = null;
var audioCacheBuffer = null;

const defaultAudioFrameSize =  1024;
const defaultVideoFrameSize = 128 * 1024;

const VideoFrameLast            = -1;
const VideoFrameFirst           = 0;
const VideoFrameMiddle          = 1;

const REQUEST_NEW_DECODER        = 0x6001;
const REQUEST_FEED_DECODER_DATA  = 0x6003;
const REQUEST_RELEASE_DECODEER   = 0x6004;

const RESPONSE_NEW_DECODER        = 0x6101;
const RESPONSE_GET_DECODED_DATA   = 0x6102;  
const RESPONSE_FEED_DECODER_DATA  = 0x6103;
const RESPONSE_RELEASE_DECODEER   = 0x6104;
const RESPONSE_FLUSH_DECODER      = 0x6105;
const RESPONSE_SETUP_DECODER      = 0x6106;  

const kEndOfStream = -9999;


var initAudioDecoder = function(decoder_type,audioSampleRate,audioChannelCount){
    if(isAudioInited) return ;
    audioCallback = Module.addFunction(function (buff, size, timestamp) {
        var index = 1;
        var outArray = Module.HEAPU8.subarray(buff, buff + size);
        var data = new Uint8Array(outArray);
        var objData = {
            type:RESPONSE_GET_DECODED_DATA,
            isAudio: true,
            data:data,
            pts:timestamp,
            index:index
        };
        self.postMessage(objData,[objData.data.buffer]);
        //console.info("get decoded audio,data_size=",data.byteLength);
    }, 'viii');
    var ret = Module._openAudioDecoder(decoder_type,audioSampleRate,audioChannelCount,audioCallback)
    if(ret == 0) {
        audioCacheBuffer = Module._malloc(defaultAudioFrameSize);
        isAudioInited = true;
        console.log("openAudioDecoder success");
    } else {
        isAudioInited = false;
        console.error("openAudioDecoder failed with error", ret);
    }
    
    return isAudioInited;
}

var initDecoder = function(isEncrypted){
    if(isDecoderInited) return ;
    var debug = 1;
    var ret =  Module._initDecoder(debug,isEncrypted? 1 : 0);
    if(ret == 0) {
        isDecoderInited = true;
        console.log("initDecoder success");
    } else {
        isDecoderInited = false;
        console.error("initDecoder failed with error", ret);
    }
    return isDecoderInited;
}

var initVideoDecoder = function(decoder_type){
    if(isVideoInited) return ;
    videoCallback = Module.addFunction(function (buff, size,width, height, pts) {
        //console.info("video callback, size = %d * %d, pts = %d", width, height, pts)
        var outArray = Module.HEAPU8.subarray(buff, buff + size);
        var data = new Uint8Array(outArray);
        var objData = {
            type:RESPONSE_GET_DECODED_DATA,
            isAudio: false,
            data:data,
            pts:pts,
            width:width,
            height:height,
            index: (kEndOfStream == pts ? -1 : 1)
        };
        self.postMessage(objData,[objData.data.buffer]);
    },"viiiii");
    //test
    var ret = Module._openVideoDecoder(decoder_type,videoCallback)
    if(ret == 0) {
        videoCacheBuffer = Module._malloc(defaultVideoFrameSize);
        isVideoInited = true;
        console.log("openVideoDecoder success");
    } else {
        isVideoInited = false;
        console.error("openVideoDecoder failed with error", ret);
    }
   return isVideoInited;
}

var release = function(){
    if(audioCacheBuffer) Module._free(audioCacheBuffer);
    if(videoCacheBuffer) Module._free(videoCacheBuffer);
    if(isVideoInited) Module._closeVideoDecoder();
    if(isAudioInited) Module._closeAudioDecoder();
    audioCacheBuffer = null;
    videoCacheBuffer = null;
    isVideoInited = false;
    isAudioInited = false;
    self.postMessage({
        type:RESPONSE_RELEASE_DECODEER
    })
    console.info("release audio/video decoder in decoder worker")
}

var flushDecoder = function(){
    Module._flushVideoDecoder();
    Module._flushAudioDecoder();
    self.postMessage({
        type:RESPONSE_FLUSH_DECODER
    })
}

self.onmessage = function(event) { // event handler for messages from the main thread
    var data = event.data
    switch(data.type){
        case REQUEST_NEW_DECODER:
           var isEncrypted = data.isEncryptedStream 
           var ret = initDecoder(isEncrypted);
             /**
             * {videoType:1}
             */
            if(ret) ret =  initVideoDecoder(data.videoType);
            /***
             * {audioType:1,sampleRate:8000,channelCount: 1}
             */
            if(ret)ret =  initAudioDecoder(data.audioType,data.sampleRate,data.channelCount);
            self.postMessage({
                type:RESPONSE_NEW_DECODER,
                ret : ret == 0
            })
            break;
        case REQUEST_FEED_DECODER_DATA:
            /**
             *  {data:data,isAudio:isAudio,pts:pts,index:index}
             */
            var pts = data.pts;
            var index = data.index;
            var isAudio = data.isAudio;
            var ret = 0;
            var data = data.data
            var typedArray = new Uint8Array(data);
            var size = typedArray.length

            if(index == VideoFrameLast){
                var objData = {
                    type:RESPONSE_GET_DECODED_DATA,
                    isAudio: false,
                    data:null,
                    pts:0,
                    width:0,
                    height:0,
                    index: VideoFrameLast
                };
                self.postMessage(objData);
                console.info("found last frame in decode worker")
                return ;
            }

            if(isAudio){
                if(size> defaultAudioFrameSize){
                    defaultAudioFrameSize = size; 
                    Module._free(audioCacheBuffer);
                    audioCacheBuffer = Module._malloc(defaultAudioFrameSize);
                }
                Module.HEAPU8.set(typedArray, audioCacheBuffer);
                ret = Module._feedAudio3(audioCacheBuffer, size, pts,1);
            }else {
                if(size> defaultVideoFrameSize){
                    defaultVideoFrameSize = size;
                    Module._free(videoCacheBuffer);
                    videoCacheBuffer = Module._malloc(defaultVideoFrameSize);
                }
                Module.HEAPU8.set(typedArray, videoCacheBuffer);
                ret = Module._feedVideo3(videoCacheBuffer, size, pts,1);
            }
            if(ret != 0)
            self.postMessage({
                type:RESPONSE_FEED_DECODER_DATA,
                isAudio:isAudio,
                ret : false
            })
            break;
        case REQUEST_RELEASE_DECODEER:
            release();
            break;
        default:
            console.info("this message type is not implemented, type= %d",data.type);
            break;           
    }
};

//const RESPONSE_SETUP_DECODER      = 0x6106;
self.postMessage({
    type:RESPONSE_SETUP_DECODER
});